package com.sinosoft.hydra.um.services;

import com.sinosoft.hydra.um.condition.SearchOrganizationCondition;
import com.sinosoft.hydra.um.condition.SearchUserCondition;
import com.sinosoft.hydra.um.constant.BaseConstant;
import com.sinosoft.hydra.um.domain.permission.SysOrganization;
import com.sinosoft.hydra.um.domain.permission.SysOrganizationUser;
import com.sinosoft.hydra.um.domain.permission.SysUserMember;
import cn.com.sinux.spring.exception.BusinessException;
import cn.com.sinux.spring.exception.ServiceException;
import com.sinosoft.hydra.um.repositories.SysOrganizationRepository;
import com.sinosoft.hydra.um.repositories.SysOrganizationUserRepository;
import com.sinosoft.hydra.um.repositories.SysUserRepository;
import cn.com.sinux.spring.utils.PageUtils;
import cn.com.sinux.spring.utils.QueryHelper;
import cn.com.sinux.spring.utils.SpecificationHelper;
import cn.com.sinux.spring.utils.ValidatorUtils;;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.criteria.*;
import java.util.*;

/**
 * 组织机构业务逻辑层interface.
 * <p>该接口文件定义了所有课题组管理有关的接口方法(比如：课题组树、课题组列表、新增课题组、保存课题组等等),另外该接口引入了
 * <code>com.sinosoft.hydra.um.vo.TreeNode</code>其中定义了ztree所需要的众多属性;
 * <code>com.sinosoft.hydra.um.domain.permission.SysOrganization</code>定义了课题组实体属性。
 * <code>org.springframework.data.domain.Page</code>定义了与翻页相关的属性。
 * <code>org.springframework.data.domain.Pageable</code>定义了分页控件常用的方法(e.g:getPageNumber(),getPageSize()等)。
 * 说明：很多方法的返回类型为Map<String,Object>是因为我们的返回值需要包括：数据(data),消息(msg),响应标识(flag),
 * 这样的好处在于：能够为前端提供更准确的返回信息。
 *
 * @author oujianlin@sinux.com.cn
 * created 2014-12-4
 * @version v0.5-alpha
 */
@Service
public class SysOrganizationService {

    @Autowired
    private SysOrganizationRepository organizationRepositories;

    @Autowired
    private SysOrganizationUserRepository sysOrganizationUserRepository;

    @Autowired
    private SysUserRepository userRepository;

    //获取用户所属课题组
    private List<SysOrganizationUser> getUserOrganizations(final String userId) {
        return sysOrganizationUserRepository.findAll(SpecificationHelper.buildSpecification(new SpecificationHelper.SpecificationBuilder<SysOrganizationUser>() {

            public void fillParam(Root<SysOrganizationUser> root, CriteriaBuilder cb, List<Predicate> list) {
                list.add(cb.equal(root.get("sysUserMember").get("id").as(String.class), userId));
            }
        }));
    }

    //获取课题组下所有用户
    private List<SysOrganizationUser> getOrganizationUsers(final String orgId) {
        return sysOrganizationUserRepository.findAll(SpecificationHelper.buildSpecification(new SpecificationHelper.SpecificationBuilder<SysOrganizationUser>() {

            public void fillParam(Root<SysOrganizationUser> root, CriteriaBuilder cb, List<Predicate> list) {
                list.add(cb.equal(root.get("sysOrganization").get("id").as(String.class), orgId));
            }
        }));
    }

    //分页查询和条件查询课题组下的所有用户
    public Page<SysOrganizationUser> findOrganizationUser(final SearchUserCondition condition, Pageable pageable) {
        if (condition == null) {
            return PageUtils.emptyPage(pageable);
        }
        Specification<SysOrganizationUser> specification = SpecificationHelper.buildSpecification(new SpecificationHelper.SpecificationBuilder<SysOrganizationUser>() {

            public void fillParam(Root<SysOrganizationUser> root, CriteriaBuilder cb, List<Predicate> list) {
                if (StringUtils.isNotBlank(condition.getUserNumber())) {
                    list.add(cb.like(root.get("sysUserMember").get("userNumber").as(String.class), "%" + condition.getUserNumber() + "%"));
                }
                if (StringUtils.isNotBlank(condition.getUserName())) {
                    list.add(cb.like(root.get("sysUserMember").get("userName").as(String.class), "%" + condition.getUserName() + "%"));
                }
                if (condition.getAvailable() != null) {
                    list.add(cb.equal(root.get("sysUserMember").get("available").as(Integer.class), condition.getAvailable()));
                }
                if (condition.getRole() != null) {
                    list.add(cb.equal(root.get("role").as(Integer.class), condition.getRole()));
                }
                if (StringUtils.isNotBlank(condition.getOrgId())) {
                    list.add(cb.equal(root.get("sysOrganization").get("id").as(String.class), condition.getOrgId()));
                }
            }
        });
        return sysOrganizationUserRepository.findAll(specification, pageable);
    }

    public Page<SysUserMember> findOrgUsers(final SearchUserCondition condition, Pageable pageable) {
        if (condition == null) {
            return PageUtils.emptyPage(pageable);
        }
        Specification<SysUserMember> specification = SpecificationHelper.buildSpecification(new SpecificationHelper.SpecificationBuilder<SysUserMember>() {

            public void fillParam(Root<SysUserMember> root, CriteriaBuilder cb, List<Predicate> list) {
                if (StringUtils.isNotBlank(condition.getUserName())) {
                    list.add(cb.like(root.get("userName").as(String.class), "%" + condition.getUserName() + "%"));
                }
                if (condition.getAvailable() != null) {
                    list.add(cb.equal(root.get("available").as(String.class), condition.getAvailable()));
                }
                if (StringUtils.isNotBlank(condition.getOrgId())) {
                    Subquery<SysUserMember> subquery = cb.createQuery().subquery(SysUserMember.class);
                    Root<SysOrganizationUser> fromProject = subquery.from(SysOrganizationUser.class);
                    subquery.select(fromProject.<SysUserMember>get("sysUserMember")); // field to map with main-query
                    SysOrganization organization = new SysOrganization();
                    organization.setId(condition.getOrgId());
                    subquery.where(cb.equal(fromProject.get("sysOrganization"), organization));
                    CriteriaBuilder.In<SysUserMember> in = cb.in(root);
                    in.value(subquery);
                    list.add(in.not());
                }
            }
        });
        return userRepository.findAll(specification, pageable);
    }

    /**
     * 设置或取消组长
     *
     * @param id
     * @param role
     * @return
     */
    @Transactional
    public List<String> leader(String id, Integer role) {
        SysOrganizationUser sysOrganizationUser = sysOrganizationUserRepository.findOne(id);
        sysOrganizationUser.setRole(role);
        sysOrganizationUserRepository.save(sysOrganizationUser);
        List<String> list = new ArrayList<String>();
        list.add(sysOrganizationUser.getSysUserMember().getUserName());
        list.add(sysOrganizationUser.getSysOrganization().getOrgName());
        return list;
    }

    /**
     * 添加课题组成员
     *
     * @param orgId
     * @param userIds
     * @param clear
     */
    @Transactional
    public List<SysUserMember> addOrgUsers(String orgId, List<String> userIds, boolean clear) {
        List<SysOrganizationUser> organizationUsers = getOrganizationUsers(orgId);
        if (clear) {
            sysOrganizationUserRepository.delete(organizationUsers);
        }
        SysOrganization sysOrganization = findOne(orgId);
        for (String userId : userIds) {
            SysUserMember sysUserMember = userRepository.findOne(userId);
            SysOrganizationUser sysOrganizationUser = new SysOrganizationUser();
            sysOrganizationUser.setRole(0);
            sysOrganizationUser.setSysUserMember(sysUserMember);
            sysOrganizationUser.setSysOrganization(sysOrganization);
            sysOrganizationUserRepository.save(sysOrganizationUser);
        }
        return userRepository.findAll(userIds);
    }

    /**
     * 删除课题组成员
     *
     * @param ids
     * @return
     */
    @Transactional
    public List<SysUserMember> deleteOrgUsers(List<String> ids) {
        List<SysOrganizationUser> sysOrganizationUsers = sysOrganizationUserRepository.findAll(ids);
        sysOrganizationUserRepository.delete(sysOrganizationUsers);
        List<String> userIds = QueryHelper.doQueryProperties(sysOrganizationUsers, true, new QueryHelper.PropertyReader<SysOrganizationUser, String>() {

            public String read(SysOrganizationUser organizationUser) {
                return organizationUser.getSysUserMember().getId();
            }
        });
        return userRepository.findAll(userIds);
    }

    /**
     * 用户管理的分配部门列表展示
     *
     * @param userId
     * @return
     */
    public String showOrganizations(String userId) {
        List<SysOrganization> list1 = organizationRepositories.findAll();
        List<SysOrganizationUser> list2 = getUserOrganizations(userId);
        StringBuffer sb = new StringBuffer();
        int i = 0;
        sb.append("[");
        for (SysOrganization organization : list1) {
            i++;
            sb.append("{\"id\":");
            sb.append("\"" + organization.getId() + "\"");
            sb.append(",");
            sb.append("\"text\":");
            sb.append("\"" + organization.getOrgName() + "\"");
            sb.append(",");
            sb.append("\"iconCls\":\"icon-group\"");
            for (SysOrganizationUser organizationUser : list2) {
                if (organizationUser.getSysOrganization().equals(organization)) {
                    sb.append(",");
                    sb.append("\"checked\":true");
                    break;
                }
            }
            sb.append("}");
            if (i < list1.size()) {
                sb.append(",");
            }
        }
        sb.append("]");
        return sb.toString();
    }

    public List<SysOrganization> showOrgs(String userId) {
        List<SysOrganization> orgs = organizationRepositories.findAll();
        List<String> ids = QueryHelper.doQueryProperties(getUserOrganizations(userId), true, new QueryHelper.PropertyReader<SysOrganizationUser, String>() {

            public String read(SysOrganizationUser orgUser) {
                return orgUser.getSysOrganization().getId();
            }
        });
        for (SysOrganization org : orgs) {
            if (ids.contains(org.getId())) {
                org.setAvailable(7);
            }
        }
        return orgs;
    }

    /**
     * 保存用户课题组
     *
     * @param userId
     * @param ids
     * @param clear
     */
    @Transactional
    public List<SysOrganization> saveUserOrganization(String userId, List<String> ids, boolean clear) {
        List<SysOrganizationUser> userOrganizations = getUserOrganizations(userId);
        if (ValidatorUtils.isArrayEmpty(ids)) {
            sysOrganizationUserRepository.delete(userOrganizations);
            return Collections.emptyList();
        }
        List<String> dest = new ArrayList<String>();
        if (clear) {
            sysOrganizationUserRepository.delete(userOrganizations);
            dest.addAll(ids);
        } else {
            for (String id : ids) {
                boolean find = false;
                for (SysOrganizationUser sysOrganizationUser : userOrganizations) {
                    if (id.equals(sysOrganizationUser.getSysOrganization().getId())) {
                        find = true;
                        break;
                    }
                }
                if (!find) {
                    dest.add(id);
                }
            }
        }
        for (String id : dest) {
            SysOrganizationUser sysOrganizationUser = new SysOrganizationUser();
            sysOrganizationUser.setRole(BaseConstant.INTEGER_FALSE);
            SysUserMember sysUserMember = new SysUserMember();
            sysUserMember.setId(userId);
            SysOrganization sysOrganization = new SysOrganization();
            sysOrganization.setId(id);
            sysOrganizationUser.setSysUserMember(sysUserMember);
            sysOrganizationUser.setSysOrganization(sysOrganization);
            sysOrganizationUserRepository.save(sysOrganizationUser);
        }
        return organizationRepositories.findAll(ids);
    }

    /**
     * 保存课题组用户
     *
     * @param orgId
     * @param ids
     * @param clear
     */
    @Transactional
    public void saveOrganizationUsers(String orgId, List<String> ids, boolean clear) {
        List<SysOrganizationUser> organizationUsers = getOrganizationUsers(orgId);
        List<String> dest = new ArrayList<String>();
        if (clear) {
            sysOrganizationUserRepository.delete(organizationUsers);
            dest.addAll(ids);
        } else {
            for (String id : ids) {
                boolean find = false;
                for (SysOrganizationUser sysOrganizationUser : organizationUsers) {
                    if (id.equals(sysOrganizationUser.getSysUserMember().getId())) {
                        find = true;
                        break;
                    }
                }
                if (!find) {
                    dest.add(id);
                }
            }
        }
        for (String id : dest) {
            SysOrganizationUser sysOrganizationUser = new SysOrganizationUser();
            sysOrganizationUser.setRole(BaseConstant.INTEGER_FALSE);
            SysUserMember sysUserMember = new SysUserMember();
            sysUserMember.setId(id);
            SysOrganization sysOrganization = new SysOrganization();
            sysOrganization.setId(orgId);
            sysOrganizationUser.setSysUserMember(sysUserMember);
            sysOrganizationUser.setSysOrganization(sysOrganization);
            sysOrganizationUserRepository.save(sysOrganizationUser);
        }
    }

    /**
     * 新增课题组
     *
     * @param org
     * @return
     */
    @Transactional
    public SysOrganization addOrganization(SysOrganization org) {
        if (null == org) {
            throw new ServiceException("课题组保存失败，参数不正确");
        }
        if (!this.checkOrgNameIsExist(org.getOrgName())) {
            org.setOrderNo(org.getOrderNo() != null ? org.getOrderNo() : 1);
            org.setCreateTime(new Date());
            if (StringUtils.isBlank(org.getOrgCode())) {
                org.setOrgCode(UUID.randomUUID().toString());
            }
            org = organizationRepositories.save(org);
        } else {
            throw new BusinessException("课题组名称已经存在，保存失败");
        }
        return org;
    }

    /**
     * 修改课题组
     *
     * @param org
     * @return
     */
    @Transactional
    public SysOrganization updateOrganization(SysOrganization org) {
        SysOrganization db = null;
        if (org == null || StringUtils.isBlank(org.getId())) {
            throw new ServiceException("保存失败(organization对象为空).");
        }
        db = organizationRepositories.findOne(org.getId());
        if (db == null) {
            throw new ServiceException("保存失败(organization对象为空).");
        }
        if (!db.getOrgName().equals(org.getOrgName()) && this.checkOrgNameIsExist(org.getOrgName())) {
            throw new ServiceException("保存失败，课题组名称已经存在");
        }
        db.setOrgName(org.getOrgName());
        db.setOrgJobs(org.getOrgJobs());
        db.setAvailable(org.getAvailable());
        organizationRepositories.save(db);
        return db;
    }

    /**
     * 批量删除课题组
     *
     * @param ids
     * @return
     */
    @Transactional
    public List<SysOrganization> deleteOrganizations(List<String> ids) {
        List<SysOrganization> sysOrganizations = organizationRepositories.findAll(ids);
        if (ValidatorUtils.isArrayNotEmpty(sysOrganizations)) {
            //先删除课题组用户
            for (SysOrganization sysOrganization : sysOrganizations) {
                sysOrganizationUserRepository.delete(sysOrganization.getSysOrganizationUsers());
            }
        }
        organizationRepositories.delete(sysOrganizations);
        return sysOrganizations;
    }

    /**
     * 查询我的课题组
     *
     * @param userId
     * @param pageable
     * @return
     */
    public Page<SysOrganizationUser> findMyOrg(final String userId, final SearchOrganizationCondition condition, Pageable pageable) {
        if (condition == null) {
            return PageUtils.emptyPage(pageable);
        }
        Specification<SysOrganizationUser> specification = SpecificationHelper.buildSpecification(new SpecificationHelper.SpecificationBuilder<SysOrganizationUser>() {

            public void fillParam(Root<SysOrganizationUser> root, CriteriaBuilder cb, List<Predicate> list) {
                list.add(cb.equal(root.get("sysUserMember").get("id").as(String.class), userId));
                if (condition.getAvailable() != null) {
                    list.add(cb.equal(root.get("sysOrganization").get("available").as(String.class), condition.getAvailable()));
                }
                if (StringUtils.isNotBlank(condition.getOrgName())) {
                    list.add(condition.isLikeWords() ?
                            cb.like(root.get("sysOrganization").get("orgName").as(String.class), "%" + condition.getOrgName() + "%")
                            : cb.equal(root.get("sysOrganization").get("orgName").as(String.class), condition.getOrgName()));
                }
            }
        });
        return sysOrganizationUserRepository.findAll(specification, pageable);
    }

    //我的课题组下的用户
    public Page<SysOrganizationUser> findMyOrgUser(final SearchUserCondition condition, Pageable pageable) {
        if (condition == null) {
            return PageUtils.emptyPage(pageable);
        }
        Specification<SysOrganizationUser> specification = SpecificationHelper.buildSpecification(new SpecificationHelper.SpecificationBuilder<SysOrganizationUser>() {

            public void fillParam(Root<SysOrganizationUser> root, CriteriaBuilder cb, List<Predicate> list) {
                if (StringUtils.isNotBlank(condition.getUserNumber())) {
                    list.add(cb.like(root.get("sysUserMember").get("userNumber").as(String.class), "%" + condition.getUserNumber() + "%"));
                }
                if (StringUtils.isNotBlank(condition.getUserName())) {
                    list.add(cb.like(root.get("sysUserMember").get("userName").as(String.class), "%" + condition.getUserName() + "%"));
                }
                if (condition.getAvailable() != null) {
                    list.add(cb.equal(root.get("sysUserMember").get("available").as(Integer.class), condition.getAvailable()));
                }
                if (condition.getRole() != null) {
                    list.add(cb.equal(root.get("role").as(Integer.class), condition.getRole()));
                }
                if (StringUtils.isNotBlank(condition.getOrgId())) {
                    list.add(cb.equal(root.get("sysOrganization").get("id").as(String.class), condition.getOrgId()));
                }
            }
        });
        return sysOrganizationUserRepository.findAll(specification, pageable);
    }

    private Specification<SysOrganization> buildOrganizationSpecification(SearchOrganizationCondition searchOrganizationCondition) {
        final SearchOrganizationCondition condition = searchOrganizationCondition == null ? new SearchOrganizationCondition() : searchOrganizationCondition;
        return SpecificationHelper.buildSpecification(new SpecificationHelper.SpecificationBuilder<SysOrganization>() {

            public void fillParam(Root<SysOrganization> root, CriteriaBuilder cb, List<Predicate> list) {
                if (condition.getAvailable() != null) {
                    list.add(cb.equal(root.get("available").as(Integer.class), condition.getAvailable()));
                }
                if (StringUtils.isNotBlank(condition.getOrgCode())) {
                    list.add(cb.equal(root.get("orgCode").as(String.class), condition.getOrgCode()));
                }
                if (StringUtils.isNotBlank(condition.getParentId())) {
                    list.add(cb.equal(root.get("parentId").as(String.class), condition.getParentId()));
                }
                if (StringUtils.isNotBlank(condition.getOrgName())) {
                    list.add(condition.isLikeWords() ?
                            cb.like(root.get("orgName").as(String.class), "%" + condition.getOrgName() + "%")
                            : cb.equal(root.get("orgName").as(String.class), condition.getOrgName()));
                }
                if (StringUtils.isNotBlank(condition.getId())) {
                    list.add(cb.equal(root.get("id").as(String.class), condition.getId()));
                }
            }
        });
    }

    /*
     * (non-Javadoc)
     * @see com.sinosoft.hydra.um.services.SysOrganizationService#findAllByPage(org.springframework.data.domain.Pageable, com.sinosoft.hydra.um.domain.permission.SysOrganization)
     */
    public Page<SysOrganization> findAll(SearchOrganizationCondition searchOrganizationCondition, Pageable pageable) {
        return organizationRepositories.findAll(buildOrganizationSpecification(searchOrganizationCondition), pageable);
    }

    /*
     * (non-Javadoc)
     * @see com.sinosoft.hydra.um.services.BaseService#findOne(java.lang.String)
     */
    public SysOrganization findOne(String id) {
        return StringUtils.isBlank(id) ? null : organizationRepositories.findOne(id);
    }

    /*
     * (non-Javadoc)
     * @see com.sinosoft.hydra.um.services.SysOrganizationService#checkOrgNameIsExist(java.lang.String)
     */
    public boolean checkOrgNameIsExist(String sysOrgName) {
        SearchOrganizationCondition searchOrganizationCondition = new SearchOrganizationCondition();
        searchOrganizationCondition.setLikeWords(false);
        searchOrganizationCondition.setOrgName(sysOrgName);
        return organizationRepositories.count(buildOrganizationSpecification(searchOrganizationCondition)) > 0;
    }

    /**
     * 用户的领导
     *
     * @param uid
     * @return
     */
    public List<SysUserMember> getLeaders(String uid) {
        SysUserMember member = userRepository.findOne(uid);
        List<SysOrganizationUser> organizations = member.getSysOrganizationUsers();
        Map<String, SysUserMember> members = new HashMap<String, SysUserMember>();
        for (SysOrganizationUser sysOrganizationUser : organizations) {
            if (!BaseConstant.INTEGER_TRUE.equals(sysOrganizationUser.getRole())) {
                SysOrganization organization = sysOrganizationUser.getSysOrganization();
                List<SysOrganizationUser> _orgUsers = organization.getSysOrganizationUsers();
                for (SysOrganizationUser _user : _orgUsers) {
                    if (BaseConstant.INTEGER_TRUE.equals(_user.getRole())) {
                        members.put(_user.getSysUserMember().getId(), _user.getSysUserMember());
                    }
                }
            }
        }
        return new ArrayList<SysUserMember>(members.values());
    }
}
